﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Mail;

using Danser_Control.Exceptions;
using Danser_Control.Managers;
using Danser_Entity.Utils;
using Danser_Entity.Entities;
using Danser_Entity.Queries.Data;
using Danser_Control.Properties;


namespace Danser_Control.ManagersImpl
{
    /// <summary>
    /// implements the IDataManager interface
    /// </summary>
    public class DataManagerImpl : IDataManager
    {
        #region C'tors

        /// <summary>
        /// creates a DataManagerImpl instance
        /// </summary>
        public DataManagerImpl() { }

        #endregion

        #region Private Fields

        private IDBManager manager = new DBManager();

        #endregion

        #region IDataManager Members

        /// <see cref="Danser_Control.Managers.IDataManager.UpdateState"/>
        public void UpdateState(String testinfoIndex, string field, bool value)
        {
            if (field.Equals(DBConstants.TEST_INFO_IS_CONFIRMED, StringComparison.CurrentCultureIgnoreCase))
            {
                manager.ConfirmTest(testinfoIndex, value);
            }
            else if (field.Equals(DBConstants.TEST_INFO_IS_ANCHOR, StringComparison.CurrentCultureIgnoreCase))
            {
                manager.AnchorTest(testinfoIndex, value);
            }
        }

        /// <see cref="Danser_Control.Managers.IDataManager.UploadInput"/>
        public IList<ValidationResult> UploadInput(IList<string> fileNames, IList<int> templateIds)
        {
            // will ho;d the validation results return from the function:
            List<ValidationResult> validationResults = new List<ValidationResult>();
            // key - mail, value - list of strings, each string has failed template for test...
            Dictionary<string, List<string>> dictFailedTestsTemplates = new Dictionary<string, List<string>>();
            IDataImporter excelDataImporter = new ExcelDataImporter();

            foreach (string fileName in fileNames)
            {
                ValidationResult vr;
                TestInfo testInfo = new TestInfo();
                TestParams testParams = new TestParams();
                List<TestResults> testResultList = new List<TestResults>();
                try
                {
                    excelDataImporter.Import(fileName, testInfo, testParams, testResultList);
                }
                // the file was in bad format, create the appropriate vr and delete the file
                catch (DanserImportException e)
                {
                    vr = new ValidationResult(fileName, false, ManagerConstants.COMMENT_BAD_FORMAT);
                    validationResults.Add(vr);
                    System.IO.File.Delete(fileName);
                    continue;
                }
                try
                {
                    // the file was in correct format, upload to the DB:
                    manager.AddTestInfo(testInfo);
                    int testParamsIndex = manager.AddTestParams(testParams);
                    SetIndexInTests(testResultList, testParamsIndex);
                    manager.AddTests(testResultList);

                    //run validation templates on the new uploaded data:
                    vr = new ValidationResult(fileName, true, "");
                    RunValidation(
                        manager,
                        testInfo.TestInfoIndex,
                        templateIds,
                        vr,
                        dictFailedTestsTemplates);
                }
                catch (DanserDuplicateKeyException e)
                {
                    vr = new ValidationResult(fileName, false, e.Message);
                }
                validationResults.Add(vr);
                System.IO.File.Delete(fileName);
            }

            // if we have tests that failed templates, send notification mails about it:
            if (dictFailedTestsTemplates.Count > 0)
            {
                SendNotificationMails(dictFailedTestsTemplates);
            }
            return validationResults;

        }

        #endregion

        #region DataManagerImpl Private functions

        /// <summary>
        /// run validation templates on test results, in case validation failed 
        /// writes it as a comment in validation results, and also adds notifications 
        /// in dictTestsTemplates so appropriate mails will be sent
        /// </summary>
        /// <param name="manager"></param> the current session of DBManager 
        /// <param name="testInfoIndex"></param> the test ID to perfrom validation on
        /// <param name="validationIds"></param> list of validation templates to validate 
        /// <param name="validationResult"></param> result validation object to write failure comments
        /// <param name="dictTestsTemplates"></param> dictionary that holds for each mail a list of notifications about tests that failed templates
        private void RunValidation(IDBManager manager, String testInfoIndex, IList<int> validationIds, ValidationResult validationResult, Dictionary<string,List<string>> dictTestsTemplates)
        {
            List<ValidationTemplate> vTemplateList = null;
            // create the filter string to retrieve the wanted templates:
            if (validationIds != null && validationIds.Count == 0)
            {
                return;
            }
            else
            {
                string filter = "";
                foreach (int vID in validationIds)
                {
                    filter += string.Format(DBConstants.VALIDATION_TEMPLATES_ID + "= {0} "+ ManagerConstants.OR_OPERATOR + " ", vID);

                }
                // remove the last or 
                filter = filter.Remove(filter.Length - (2 + ManagerConstants.OR_OPERATOR.Length));
                vTemplateList = manager.GetValidationTemplates(filter);
            }

            // for each template, run a validation check on the new data:
            foreach (ValidationTemplate vTemplate in vTemplateList)
            {
                if (false == manager.ValidateTest(testInfoIndex, vTemplate.Expression))
                {
                    // set the failed connection between the test and template:
                    manager.SetFailedTest(vTemplate.ID, testInfoIndex);
                    // add the failed notice to the files comment:
                    validationResult._comment += String.Format(ManagerConstants.COMMENT_TEMPLATE_FAILED, vTemplate.Name);
                    // for each mail that should be sent, add (or alter) the value for this mail in the dictionary:
                    string [] mails = vTemplate.MailList.Split(';');
                    string toAdd = string.Format(ManagerConstants.MAIL_FORMAT_FAIL, testInfoIndex, vTemplate.Name);
                    for (int i = 0; i < mails.Length; i++)
                    {
                        List<string> failedList;
                        dictTestsTemplates.TryGetValue(mails[i], out failedList);
                        if (null == failedList) // no such value is there
                        {
                            // create a list to hold failed notifications:
                            failedList = new List<string>();
                            failedList.Add(toAdd);
                            dictTestsTemplates.Add(mails[i], failedList);
                        }
                        else // a mail is already there
                        {
                            // add the new notification to the existing list:
                            failedList.Add(toAdd);
                            dictTestsTemplates[mails[i]] = failedList;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// send notification mails to all relevant mails
        /// </summary>
        /// <param name="dictTestsTemplates">dictionary holdind for each mail (key) what notification to send it</param> 
        private void SendNotificationMails(Dictionary<string, List<string>> dictTestsTemplates)
        {
            Dictionary<string, List<string>>.KeyCollection keys = dictTestsTemplates.Keys;
            foreach (string key in keys)
            {
                SendMail(key, dictTestsTemplates[key]);
            }
        }

        /// <summary>
        /// sends a specific mail
        /// </summary>
        /// <param name="strMail"></param> the To mail
        /// <param name="failedTests"></param> the notifications to send
        private void SendMail(string strMail, List<string> failedTests)
        {
            try
            {
                // create the mail message:
                MailMessage mail = new MailMessage();
                mail.To.Add(strMail);
                mail.From = new MailAddress(Settings.Default.NotificationMail);
                mail.Subject = ManagerConstants.MAIL_SUBJECT;
                mail.Body = GenerateMailBody(failedTests);
                mail.IsBodyHtml = true;
                AlternateView htmlView = AlternateView.CreateAlternateViewFromString(
                    mail.Body,
                    null,
                    "text/html");
                LinkedResource imgLink = new LinkedResource(
                    Settings.Default.ImageLocation,
                    "image/png");
                imgLink.ContentId = "Title";
                imgLink.TransferEncoding = System.Net.Mime.TransferEncoding.Base64;
                htmlView.LinkedResources.Add(imgLink);
                mail.AlternateViews.Add(htmlView);

                // configure the smtp client:
                SmtpClient smtp = new SmtpClient();
                smtp.Host = Settings.Default.SmtpHost;
                smtp.Credentials = new System.Net.NetworkCredential(
                    Settings.Default.NotificationMail, 
                    Settings.Default.NotificationMailPwd);
                smtp.EnableSsl = true;
                smtp.Send(mail);

            }
            catch
            {
            }
        }

        /// <summary>
        /// Generates the mail body in html format
        /// </summary>
        /// <param name="failedTests"></param>
        /// <returns></returns>
        private string GenerateMailBody(List<string> failedTests)
        {
            StringBuilder body = new StringBuilder(ManagerConstants.MAIL_HEADER);
            foreach (string failure in failedTests)
            {
                body.Append(failure);
            }
            body.Append(ManagerConstants.MAIL_END);
            return body.ToString();
        }

        /// <summary>
        /// Sets the TestParamsIndex attribute in each of the given tests to be the given testParamsIndex
        /// </summary>
        /// <param name="testResultList"></param>
        /// <param name="testParamsIndex"></param>
        private void SetIndexInTests(List<TestResults> testResultList, int testParamsIndex)
        {
            foreach (TestResults tResults in testResultList)
            {
                tResults.TestParamsIndex = testParamsIndex;
            }
        }

        #endregion
    }
}
